// Vector 3d
// (c) jimon game studio

#ifndef JEH_JEVECTOR3D
#define JEH_JEVECTOR3D

#include "jeTypes.h"
#include "jeMath.h"

namespace je
{
	//! Vector 3d class
	template<typename T>
	class jeVector3d
	{
	public:
		//! Components
		T X,Y,Z;

		//! Constructor
		inline jeVector3d()
			:X(0),Y(0),Z(0)
		{
		}

		//! Constructor
		inline jeVector3d(T x,T y,T z)
			:X(x),Y(y),Z(z)
		{
		}

		//! Constructor
		inline jeVector3d(T k)
			:X(k),Y(k),Z(k)
		{
		}

		//! Flush Vector (set 0,0,0)
		inline void Flush()
		{
			X = 0;
			Y = 0;
			Z = 0;
		}

		//! Constructor
		template <class B>
		inline jeVector3d(const jeVector3d<B> & Other)
			:X((T)Other.X),Y((T)Other.Y),Z((T)Other.Z)
		{
		}

		//! Set X Y Z
		inline void SetXYZ(T x,T y,T z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		//! Normalize Vector
		inline jeVector3d<T> & Normalize()
		{
			T DistS = X*X + Y*Y + Z*Z;

			if(DistS == 0)
				return *this;

			DistS = (T) jeInvSqr(DistS);

			X *= DistS;
			Y *= DistS;
			Z *= DistS;

			return *this;
		}

		//! operator =
		inline jeVector3d<T> & operator = (const jeVector3d<T> & Other)
		{
			X = Other.X;
			Y = Other.Y;
			Z = Other.Z;
			return *this;
		}
		
		//! operator =
		template<typename B>
		inline jeVector3d<T> & operator = (const jeVector3d<B> & Other)
		{
			X = (T)Other.X;
			Y = (T)Other.Y;
			Z = (T)Other.Z;
			return *this;
		}

		//! return true if our vector == other vector
		inline u1 equal(const jeVector3d<T> & Other) const
		{
			if (X == Other.X && Y == Other.Y && Z == Other.Z)
				return true;
			else
				return false;
		}

		//! operator ==
		inline u1 operator == (const jeVector3d<T> & Other) const
		{
			return equal(Other);
		}

		//! operator !=
		inline u1 operator != (const jeVector3d<T> & Other) const
		{
			return !equal(Other);
		}

		//! operator +=
		inline jeVector3d<T> & operator += (const jeVector3d<T> & Other)
		{
			X += Other.X;
			Y += Other.Y;
			Z += Other.Z;
			return *this;
		}

		//! operator -=
		inline jeVector3d<T> & operator -= (const jeVector3d<T> & Other)
		{
			X -= Other.X;
			Y -= Other.Y;
			Z -= Other.Z;
			return *this;
		}

		//! operator +
		inline jeVector3d<T> operator + (const jeVector3d<T> & Other) const
		{
			return jeVector3d<T>(X + Other.X,Y + Other.Y,Z + Other.Z);
		}
		
		//! operator -
		inline jeVector3d<T> operator - (const jeVector3d<T> & Other) const
		{
			return jeVector3d<T>(X - Other.X,Y - Other.Y,Z - Other.Z);
		}

		//! operator *
		inline jeVector3d<T> operator * (T Value) const
		{
			return jeVector3d<T>(X * Value,Y * Value,Z * Value);
		}

		//! operator *
		inline jeVector3d<T> operator * (const jeVector3d<T> & Other) const
		{
			return jeVector3d<T>(X * Other.X,Y * Other.Y,Z * Other.Z);
		}

		//! operator /
		inline jeVector3d<T> operator / (T Value) const
		{
			if(Value == 0)
				Value = 0.01f;
			return jeVector3d<T>(X / Value,Y / Value,Z / Value);
		}

		//! operator /
		inline jeVector3d<T> operator / (const jeVector3d<T> & Other) const
		{
			jeVector3d<T> Cur;
			if(Other == jeVector3d<T>(0,0,0))
				Cur = jeVector3d<T>(0.01f,0.01f,0.01f);
			else
				Cur = Other;
			return jeVector3d<T>(X / Cur.X,Y / Cur.Y,Z / Cur.Z);
		}
		
		//! Get dot product
		T DotProduct(const jeVector3d<T> & Other) const
		{
			return X * Other.X + Y * Other.Y + Z * Other.Z;
		}

		//! Get cross product
		jeVector3d<T> CrossProduct(const jeVector3d<T> & Other) const
		{
			return jeVector3d<T>(Y * Other.Z - Z * Other.Y, Z * Other.X - X * Other.Z, X * Other.Y - Y * Other.X);
		}

		//! Get Distance
		inline f32 GetDistance() const
		{
			return jeSqr(X*X+Y*Y+Z*Z);
		}

		//! Get Distance
		inline f32 GetDistanceQR() const
		{
			return X*X+Y*Y+Z*Z;
		}

		//! Get Max Component
		inline T GetMaxComponent() const
		{
			return jeMax(X,Y,Z);
		}

		//! Get Min Component
		inline T GetMinComponent() const
		{
			return jeMax(X,Y,Z);
		}

		//! Linear Interpolation
		jeVector3d<T> LinearInterpolation(jeVector3d<T> B,f32 Time)
		{
			jeVector3d<T> A = *this;
			jeVector3d<T> C = A * (1 - Time) + B * Time;
			return C;
		}

	};

	//! define float 32 3d vector
	typedef jeVector3d<f32> jeVector3df;

	//! define int 32 3d vector
	typedef jeVector3d<s32> jeVector3di;

};

#endif
